<template>
    <div class="page-box">
        <el-form class="config" label-width="100px">
            <el-form-item label="网址" class="link-input">
                <el-input
                    v-model="qrcodeConfig.content"
                    type="textarea"
                    placeholder="https://"
                    maxlength="2000"
                    rows="4"
                    :disabled="shortLinkLoading"
                ></el-input>
            </el-form-item>

            <el-form-item label="二维码Logo">
                <ImgUpload @getImage="getImage"></ImgUpload>
            </el-form-item>

            <el-form-item label="Logo效果" v-show="originalInnerImage">
                <stroke-selector
                  v-model:image="qrcodeConfig.innerImage"
                  :originalImage="originalInnerImage"
                ></stroke-selector>
            </el-form-item>
        </el-form>
        <div>
            <div class="preview" v-loading="dataUrl && qrcodeLoading">
                <span v-if="!dataUrl" class="desc">二维码生成区域</span>
                <img
                    v-else
                    :src="qrCodeImageURL"
                    class="qrcode"
                >
            </div>
            <template v-if="dataUrl">
                <correction-selector label="容错" v-model:targetLevel="qrcodeConfig.correctionLevel">
                </correction-selector>

                <image-slider label="尺寸" v-model:size="qrcodeConfig.size">
                </image-slider>

                <code-color-picker
                  label="码点颜色"
                  v-model:color="qrcodeConfig.darkColor"
                ></code-color-picker>
                <code-color-picker
                  label="背景颜色"
                  v-model:color="qrcodeConfig.lightColor">
                </code-color-picker>
                <el-button
                    type="primary"
                    class="export-btn"
                    @click="exportQRcode"
                >
                    下载二维码
                </el-button>

                <div class="tip-text">
                    {{ tipText }}
                    <el-button v-if="isAtypia" type="text" @click="autoFixContent">
                        (点击这里自动修复)
                    </el-button>
                </div>

            </template>
            <canvas
                ref="exportCanvas"
                class="export-canvas"
                :width="qrcodeConfig.size"
                :height="qrcodeConfig.size"
            ></canvas>
        </div>
    </div>
</template>

<script lang="ts">
import {
  Options, Vue, Ref, Watch,
} from 'vue-property-decorator';
import {
  ElForm, ElFormItem, ElInput, ElButton, ElInputNumber, ElMessage,
} from 'element-plus';
import { Debounce, rgba2hex } from '@/common/utils';
import QRCode from 'qrcode';
import { downloadImage, drawImageToCanvas, loadImage } from '../../utils';
import ImgUpload from '../img-upload.vue';
import CorrectionSelector from './components/correction-selector.vue';
import ImageSlider from './components/innerimage-slider.vue';
import CodeColorPicker from './components/code-color-picker.vue';
import StrokeSelector from './components/stroke-selector.vue';

const enum QRCONTEXT_STATUS { NORMAL = 'normal', INTENSIVE = 'intensive', ATYPIA = 'atypia' }

type CorrectionLevelType = 'L' | 'M' | 'Q' | 'H';

interface IQrcodeConfig {
    content: string;
    size: number;
    innerImage?: string;
    correctionLevel: CorrectionLevelType;
    darkColor?: string; // rgba
    lightColor?: string;
}

@Options({
  components: {
    ElForm,
    ElFormItem,
    ElInput,
    ElButton,
    ElInputNumber,
    ImgUpload,
    CorrectionSelector,
    ImageSlider,
    CodeColorPicker,
    StrokeSelector,
  },
})
export default class QRcodeGenerate extends Vue {
    @Ref('exportCanvas')
    exportCanvas!: HTMLCanvasElement;

    dataUrl = ''; // 最终生成二维码的Base64数据

    qrCodeImageURL = '';

    qrcodeLoading = false;

    originalInnerImage = ''; // 内嵌图片原图

    qrcodeConfig: IQrcodeConfig = {
      content: this.dataInRouter,
      size: 200,
      innerImage: '', // 内嵌图片的base64数据
      correctionLevel: 'Q',
      darkColor: 'rgb(0,0,0)',
      lightColor: 'rgb(255,255,255)',
    };

    shortLinkLoading = false;

    firstEnter = true;

    get qrContext() {
      return this.qrcodeConfig.content;
    }

    get dataInRouter() {
      const data = this.$route?.query?.data as string;
      return decodeURIComponent(data || '');
    }

    get qrErrorCorrectionLevel():CorrectionLevelType {
      return this.qrcodeConfig.innerImage ? 'Q' : 'L';
    }

    get innerImageSizeMap(): Record<CorrectionLevelType, number> {
      return {
        L: 0.1,
        M: 0.15,
        Q: 0.20,
        H: 0.25,
      };
    }

    get qrContextStatus() {
      const reg = /\r|\n|\s/;
      const { content, size } = this.qrcodeConfig;
      if (content.length && size / content.length < 1) {
        return QRCONTEXT_STATUS.INTENSIVE;
      }
      if (reg.test(this.qrContext)) {
        return QRCONTEXT_STATUS.ATYPIA;
      }
      return QRCONTEXT_STATUS.NORMAL;
    }

    get tipText(): string {
      const TextMap = {
        [QRCONTEXT_STATUS.NORMAL]: '为保证使用体验，请使用移动设备扫码验证后再进行投放',
        [QRCONTEXT_STATUS.INTENSIVE]: '当前二维码信息较多，必要时请增加二维码尺寸',
        [QRCONTEXT_STATUS.ATYPIA]: '当前网址信息中含有空格或换行,可能导致无法正常跳转',
      };
      return TextMap[this.qrContextStatus];
    }

    get isAtypia(): boolean {
      return this.qrContextStatus === QRCONTEXT_STATUS.ATYPIA;
    }

    @Debounce(600)
    @Watch('qrcodeConfig', { deep: true })
    generateQRcode() {
      if (!this.qrContext && !this.firstEnter) {
        return ElMessage.error('请输入二维码数据');
      }
      this.qrcodeLoading = true;
      setTimeout(async () => { // 这里的300ms延迟主要是为了优化重新渲染二维码时的loading态体验
        this.qrCodeImageURL = await this.getRecommendSize();
        this.qrcodeLoading = false;
      }, 300);
      this.firstEnter = false;
      return undefined;
    }

    getImage(data: string) {
      this.originalInnerImage = data;
      this.qrcodeConfig.innerImage = this.originalInnerImage;
    }

    exportQRcode() {
      const fileName = `二维码-${new Date().toLocaleString()}.png`;
      const download = downloadImage(fileName, this.qrCodeImageURL);
      if (!download) {
        ElMessage.error('下载失败，请稍后再试');
      }
    }

    async drawQRcodeCanvas(canvas: HTMLCanvasElement, size: number) {
      await drawImageToCanvas(canvas, this.dataUrl, {
        dx: 0,
        dy: 0,
        dw: size,
        dh: size,
      });
      await this.computeInnerImage(canvas, size);
      return canvas.toDataURL('image/png');
    }

    async computeInnerImage(canvas: HTMLCanvasElement, size: number) {
      const { correctionLevel, innerImage } = this.qrcodeConfig;
      if (innerImage) {
        const image = await loadImage(innerImage);
        const { width, height } = image;
        const innerImageSizeRate = this.innerImageSizeMap[correctionLevel];
        const innerSize = size * innerImageSizeRate;
        const sizeRate = innerSize / Math.max(width, height);
        const innerWidth = width * sizeRate;
        const innerHeight = height * sizeRate;

        await drawImageToCanvas(canvas, innerImage, {
          dx: (size - innerWidth) / 2,
          dy: (size - innerHeight) / 2,
          dw: innerWidth,
          dh: innerHeight,
        });
      }
    }

    async getRecommendSize(): Promise<string> {
      const {
        size,
        correctionLevel,
        darkColor = 'rgb(0,0,0)',
        lightColor = 'rgb(255,255,255)',
      } = this.qrcodeConfig;
      this.dataUrl = await QRCode.toDataURL(this.qrContext, {
        width: size,
        errorCorrectionLevel: correctionLevel,
        color: {
          dark: rgba2hex(darkColor),
          light: rgba2hex(lightColor),
        },
      });
      const imageURL = await this.drawQRcodeCanvas(this.exportCanvas, size);
      return imageURL;
    }

    private autoFixContent(): void {
      const rewriteContent = this.qrContext.replace(/\r|\n|\s/g, '');
      this.qrcodeConfig.content = rewriteContent;
    }

    private changeCorrectionLevel(correctionLevel: CorrectionLevelType): void {
      this.qrcodeConfig.correctionLevel = correctionLevel;
    }

    private changeSize(size: number): void {
      this.qrcodeConfig.size = size;
    }
}
</script>

<style lang='less' scoped>
    .page-box{
        display: flex;
        // align-items: center;
        .config{
            flex: 1;
            margin-right: 10px;
            :deep(.el-textarea__inner) {
                padding-bottom: 18px;
            }
            .link-input {
                position: relative;
                .to-short-btn {
                    position: absolute;
                    padding: 5px;
                    bottom: 1px;
                    left: 10px;
                    background: #fff;
                    font-size: 12px;
                    color: #999;
                    &:hover {
                        color: #595959;
                    }
                    .transition {
                        display: inline-block;
                        margin-right: 4px;
                    }
                }
            }
        }
        .preview{
            display: flex;
            align-items: center;
            justify-content: center;
            width:220px;
            height: 220px;
            background: #fafafa;
            border: 1px dashed #ccc;
            .qrcode{
                width: 100%;
                height: 100%;
            }
        }
        .export-btn{
            width: 100%;
            margin-top: 10px;
        }
        .desc{
            color: #bbb;
            font-size: 14px;
            font-weight: 500;
        }
        .content-input{
            :deep(.el-input__inner){
                height: 100%;
                font-size: 18px;
            }
            height: 50px;
            margin-bottom: 10px;
        }
        .generate-btn{
            font-size: 16px;
            font-weight: 500;
        }
        .export-canvas{
            position:fixed;
            left:100%;
        }
        .disabled {
            background: #fafafa!important;
        }
        .tip-text{
            max-width: 220px;
            margin-top: 10px;
            color: #999;
            em {
                font-style: normal;
                text-decoration: underline;
                text-decoration-style: double;
            }
        }

    }

</style>
